package com.dragee.modules.sys.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.dragee.common.R;
import com.dragee.common.VerifyParam;
import com.dragee.common.core.domain.AjaxResult;
import com.dragee.config.exception.AuthErrorType;
import com.dragee.config.exception.AuthException;
import com.dragee.config.exception.BusinessErrorType;
import com.dragee.config.exception.BusinessException;
import com.dragee.config.resolver.User;
import com.dragee.config.shiro.AuthToken;
import com.dragee.constants.AuthConstants;
import com.dragee.modules.base.entity.SmsCodeEntity;
import com.dragee.modules.base.service.SmsCodeService;
import com.dragee.modules.sys.dto.TokenDTO;
import com.dragee.modules.sys.entity.*;
import com.dragee.modules.sys.po.RolePo;
import com.dragee.modules.sys.service.ISysMenuService;
import com.dragee.modules.sys.service.ISysRoleService;
import com.dragee.modules.sys.service.UserRoleService;
import com.dragee.modules.sys.service.UserService;
import com.dragee.modules.sys.service.impl.PermissionService;
import com.dragee.modules.sys.vo.*;
import com.dragee.modules.work.entity.ApplyProjectEntity;
import com.dragee.modules.work.entity.CompetitionOrderEntity;
import com.dragee.modules.work.entity.CompetitionOrderRefereeEntity;
import com.dragee.modules.work.entity.TeamEntity;
import com.dragee.modules.work.service.*;
import com.dragee.utils.LoginUserUtil;
import com.dragee.utils.TokenUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import java.util.*;

/**
 * @author tys on 11/10/20
 */
@RestController
@Api(tags = "SYS - 登陆")
public class LoginController {

    @Autowired
    private SmsCodeService smsCodeService;
    @Autowired
    private UserService userService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private ISysMenuService menuService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private TeamService teamService;
    @Autowired
    private CompetitionOrderRefereeService competitionOrderRefereeService;
    @Autowired
    private CompetitionService competitionService;
    @Autowired
    private CompetitionOrderService competitionOrderService;
    @Autowired
    private ApplyProjectService applyProjectService;
    @Autowired
    private ApplyService applyService;

    public static Map<String, String> codeType;

    static {
        codeType = new HashMap<>();
        codeType.put("login", "login");
        codeType.put("updatePwd", "updatePwd");
    }

    @GetMapping("user-info")
    @ApiOperation("获取当前登陆人基本信息")
    public R userInfo (@User @ApiIgnore LoginUser user) {
        UserEntity u = userService.getById(user.getUserId());
        String roleKey = user.getRole().getRoleKey();
        u.setRoleKey(roleKey);
        if ("duiwu".equals(roleKey)) {
            TeamEntity team = teamService.getTeamByUserId(u.getUserId().toString());
            u.setTeamId(team.getCode());
        }
        return R.ok().put("data", u);
    }

    @GetMapping("getInfo")
    @ApiOperation("获取当前登陆人权限和菜单")
    public AjaxResult userInfo () {
        LoginUser loginUser = LoginUserUtil.getLoginUser();
        UserEntity user = userService.getById(loginUser.getUserId());
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(loginUser.getUserId());
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermissions(loginUser.getUserId());
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", loginUser);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        ajax.put("avatar",user.getAvatar());
        return ajax;
    }



    @ApiOperation("用户注册")
    @PostMapping("/registered")
    public R registered (@RequestBody RegisteredVo vo) {
        /*参数校验*/
        VerifyParam.notNull(vo);
        /*客户端校验*/
        if (!Arrays.asList(AuthConstants.REGISTER_CLIENTS).contains(vo.getType())) {
            throw new AuthException(AuthErrorType.NO_SUPPORT_CLIENT);
        }
        /*判断用户是否存在*/
        int count = userService.count(new QueryWrapper<UserEntity>().eq("username", vo.getUsername()));
        if (count > 0) {
            throw new AuthException(AuthErrorType.USERNAME_ALEARY_REGISTERED);
        }

        UserEntity userEntity = new UserEntity();
        userEntity.setUsername(vo.getUsername());
        userEntity.setName(vo.getName());
        userEntity.setPassword(new Sha256Hash(vo.getPassword(), AuthConstants.SLAT_PWD).toHex());
        userEntity.setProvince(vo.getProvince());
        userEntity.setStatus("1");
        userService.save(userEntity);

        /*增加角色*/
        SysRole role = sysRoleService.getRoleByKey(vo.getType());
        if (role == null) {
            throw new AuthException(801, "角色不存在");
        }
        userRoleService.save(new SysUserRole(userEntity.getUserId(), role.getRoleId()));

        //队伍注册的话 则增加队伍信息
        if (vo.getType().equals("duiwu")) {
            TeamEntity team = new TeamEntity();
            team.setCode(vo.getTeamid());
            team.setUserId(userEntity.getUserId().toString());
            teamService.save(team);
        }
        return R.ok();
    }

    @ApiOperation("短信登录")
    @PostMapping("/login-sms")
    public R loginSms (@RequestBody LoginSmsVo vo) throws Exception {
        /*参数校验*/
        VerifyParam.notNull(vo);
        /*客户端校验*/
        if (!Arrays.asList(AuthConstants.CLIENTS).contains(vo.getType())) {
            throw new AuthException(AuthErrorType.NO_SUPPORT_CLIENT);
        }
        /*验证码校验*/
        verifySmsCode(vo.getUsername(), vo.getVerifyCode(), "login");
        boolean ifRegistered = ifRegistered(vo.getUsername());
        if (!ifRegistered) {
            return R.ok().put("data", JSONUtil.createObj().set("ifRegistered", false));
        }

        LoginUser loginUser = userService.getLoginUser(vo.getUsername(), null);
        loginUser.setType(vo.getType());
        setRole(loginUser);
        loginUser.setPermissions(permissionService.getMenuPermissions(loginUser.getUserId()));
        /*登录*/
        TokenDTO tokenDto = dealToken(loginUser);
        return R.ok().put("data", tokenDto);
    }


    @ApiOperation("裁判登录")
    @PostMapping("/login-referee")
    public R loginReferee (@RequestBody LoginRefereeVo vo) throws Exception {
        /*参数校验*/
        VerifyParam.notNull(vo);

        /*密码校验*/
        LoginUser loginUser = competitionOrderRefereeService.checkOrderReferee(vo);
        /*登录*/
        TokenDTO tokenDto = dealToken(loginUser);
        return R.ok().put("data", tokenDto);
    }

    @GetMapping("getRefereeInfo")
    @ApiOperation("获取裁判的信息")
    public AjaxResult getRefereeInfo () {
        LoginUser loginUser = LoginUserUtil.getLoginUser();
        loginUser.getUserId();
        CompetitionOrderRefereeEntity referee = competitionOrderRefereeService.getById(loginUser.getUserId());
        AjaxResult ajax = AjaxResult.success();
        CompetitionOrderEntity order = competitionOrderService.getById(referee.getOrderId());
        ajax.put("user", loginUser);
        ajax.put("name", referee.getName());
        ajax.put("idCard", referee.getIdCard());
        ajax.put("type", referee.getType());
        ajax.put("competitionId", referee.getCompetitionId());
        ajax.put("orderId", referee.getOrderId());
        ajax.put("competition", competitionService.getById(referee.getCompetitionId()).getName());
        ajax.put("order", order.getTitle());
        String[] apIds = order.getApplyProjectIds().split(",");
        List<ApplyProjectEntity> aps =
                applyProjectService.list(new QueryWrapper<ApplyProjectEntity>().in("id", apIds));
        for (ApplyProjectEntity ap : aps) {
            applyService.setApplyProject(ap);
        }
        ajax.put("applyProject", aps);
        return ajax;
    }




    @ApiOperation("密码登陆")
    @PostMapping("/login-pwd")
    public R loginPwd (@RequestBody LoginPwdVo vo) throws Exception {
        /*参数校验*/
        VerifyParam.notNull(vo);
        /*客户端校验*/
        if (!Arrays.asList(AuthConstants.CLIENTS).contains(vo.getType())) {
            throw new AuthException(AuthErrorType.NO_SUPPORT_CLIENT);
        }
        /*账号是否注册校验*/
        boolean ifRegistered = ifRegistered(vo.getUsername());
        if (!ifRegistered) {
            return R.ok().put("data", JSONUtil.createObj().set("ifRegistered", false));
        }
        /*密码校验*/
        LoginUser loginUser = verifyPassword(vo);
        setRole(loginUser);
        loginUser.setPermissions(permissionService.getMenuPermissions(loginUser.getUserId()));
        /*登录*/
        TokenDTO tokenDto = dealToken(loginUser);
        return R.ok().put("data", tokenDto);
    }

    private void setRole (LoginUser user) {
        SysRole role = sysRoleService.getRoleByUserId(user.getUserId().toString());
        if (role == null) {
            throw new AuthException(AuthErrorType.USERNAME_NO_ROLE_ERROR);
        }
        if (!role.getRoleKey().equals(user.getType())) {
            throw new AuthException(801, "当前账号无权限登录该角色");
        }
        user.setRole(new RolePo(role));
    }

    @ApiOperation("修改密码")
    @PostMapping("/update-pwd")
    public R updatePwd (@RequestBody UpdatePwdVo vo) {
        /*参数校验*/
        VerifyParam.notNull(vo);
        /*设置加密密码*/
        String pwd = new Sha256Hash(vo.getPassword(), AuthConstants.SLAT_PWD).toHex();
        /*修改密码*/
        boolean result = userService.update(new UpdateWrapper<UserEntity>()
                .set("password", pwd).eq("username", vo.getUsername()));
        if (!result) {
            throw new AuthException(AuthErrorType.UPDATE_PASSWORD_ERROR);
        }
        return R.ok();
    }

//    @ApiOperation("绑定新手机")
//    @PostMapping("/bind-new-tel")
//    public R bindNewTel (@RequestBody BindNewTelVo vo) {
//        /*参数校验*/
//        VerifyParam.notNull(vo);
//        /*新手机的验证码校验*/
//        verifySmsCode(vo.getNewUserName(), vo.getCode(), "bindTel");
//        /*修改手机*/
//        boolean result = wccUserService.bindNewTel(vo.getNewUserName(), vo.getOldUserName());
//        if (!result)
//            throw new AuthException(AuthErrorType.BIND_TEL_ERROR);
//        return R.ok();
//    }

    @GetMapping("getRouters")
    @ApiOperation("获取Router")
    public AjaxResult getRouters()
    {
        Long userId = LoginUserUtil.getLoginUser().getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }

    @ApiOperation("获取短信验证码")
    @PostMapping("/sms-code")
    public R smsCode (String username, String smsType) throws Exception {
        if (StrUtil.isBlank(username) || StrUtil.isBlank(smsType)) {
            throw new BusinessException(BusinessErrorType.INVALID_PARAMS);
        }
        String code = ((int)((Math.random()*9+1)*100000)) + "";
        smsCodeService.save(new SmsCodeEntity(username+":"+codeType.get(smsType), code, System.currentTimeMillis() + 3600L));
        // String respCode = SmsUtil.sendCode(username, code, type);
        String respCode = "OK";
        if ("OK".equals(respCode)){
            return R.ok().put("data", JSONUtil.createObj().set("code", code));
        }else {
            return R.error().put("code",respCode).put("msg","短信发送失败！");
        }

    }

    @ApiOperation("校验验证码")
    @GetMapping("/verify-sms-code")
    public R UserVerifySmsCode (String username, String smsCode, String smsType) {
        if (StrUtil.isBlank(username) || StrUtil.isBlank(smsType) || StrUtil.isBlank(smsCode)) {
            throw new BusinessException(BusinessErrorType.INVALID_PARAMS);
        }
        verifySmsCode(username, smsCode, smsType);
        return R.ok();
    }



    /** 校验验证码
     * @param username
     */
    private void verifySmsCode (String username, String smsCode, String type) {
        SmsCodeEntity resultCode = smsCodeService.getOne(new QueryWrapper<SmsCodeEntity>()
                .eq("flag", username + ":" + type)
                .eq("code", smsCode)
                .lt("invaidTime", System.currentTimeMillis()), false);
        if (resultCode == null || !resultCode.equals(resultCode)) {
            throw new AuthException(AuthErrorType.SMS_CODE_ERROR);
        }
    }


    /** 用户名是否注册
     * @param userName
     * @return
     */
    private boolean ifRegistered (String userName) {
        int count = userService.count(new QueryWrapper<UserEntity>().eq("username", userName));
        return count > 0 ? true : false;
    }

    /** 密码校验 如果成功返回用户
     * @param vo
     * @return
     */
    private LoginUser verifyPassword (LoginPwdVo vo) {
        String password = new Sha256Hash(vo.getPassword(), AuthConstants.SLAT_PWD).toHex();
        LoginUser loginUser = userService.getLoginUser(vo.getUsername(), password);
        if (loginUser == null) {
            throw new AuthException(AuthErrorType.PASSWORD_ERROR);
        }
        loginUser.setType(vo.getType());
        return loginUser;
    }

    /** 处理token
     * @param loginUser
     * @return
     */
    private TokenDTO dealToken (LoginUser loginUser) throws Exception {
        String tokenStr;
        AuthenticationToken token;
        Subject subject = SecurityUtils.getSubject();
        // 将loginUser对象放到token中
        tokenStr = TokenUtil.createToken(loginUser, AuthConstants.TOKEN_EXPIRE);
        // 用户登陆 返回token
        token = new AuthToken(tokenStr);
        /*核心 调用认证方法*/
        try  {
            subject.login(token);
            String roleName = "";
            if (loginUser.getRole() != null) {
                roleName = loginUser.getRole().getRoleName();
            }
            TokenDTO dto = new TokenDTO(AuthConstants.JWT_TOKEN_HEADER,
                    AuthConstants.JWT_TOKEN_PREFIX + tokenStr,
                    loginUser.getUserId(),
                    loginUser.getUsername(),
                    roleName,
                    loginUser.getRole());

            return dto;
        } catch (BusinessException e) {
            throw new BusinessException(e.getCode(), e.getMessage());
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }


//    @ApiOperation("修改密码")
//    @PostMapping("/update-password")
//    public R updatePassword (@RequestBody UpdatePasswordVo vo) {
//
//        // 传入的密码加密
//        String password = new Sha256Hash(vo.getOldPassword(), AuthConstants.SLAT_PWD).toHex();
//        // 根据用户名密码去数据库比对用户 获取LoginUser
//        LoginUser loginUser = wccUserService.getLoginUser(vo.getUsername(), password);
//        if (loginUser == null) {
//            //比对失败
//            throw new AuthException(AuthErrorType.USERNAME_PASSWORD_ERROR);
//        }
//        wccUserService.updatePassword(vo);
//        return R.ok();
//    }


}


